package com.zhangxin.aiInterview.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhangxin.aiInterview.common.ErrorCode;
import com.zhangxin.aiInterview.constant.FileConstant;
import com.zhangxin.aiInterview.exception.BusinessException;
import com.zhangxin.aiInterview.manager.work_flow.resume_analyze.ResumeAnalyze;
import com.zhangxin.aiInterview.manager.work_flow.resume_content_extraction.ResumeContentExtraction;
import com.zhangxin.aiInterview.manager.cos.CosManager;
import com.zhangxin.aiInterview.mapper.ResumeMapper;
import com.zhangxin.aiInterview.model.dto.resume.ResumeData;
import com.zhangxin.aiInterview.model.dto.resume.ResumeSearchRequest;
import com.zhangxin.aiInterview.model.dto.resume.UploadFileRequest;
import com.zhangxin.aiInterview.model.entity.*;
import com.zhangxin.aiInterview.model.entity.User;
import com.zhangxin.aiInterview.model.enums.AnalyzeStatusEnum;
import com.zhangxin.aiInterview.model.enums.IsFullInterviewEnum;
import com.zhangxin.aiInterview.model.po.ExtractedResumePO;
import com.zhangxin.aiInterview.model.vo.ResumeVO;
import com.zhangxin.aiInterview.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author ZX
 * @description 针对表【resume(简历表)】的数据库操作Service实现
 * @createDate 2025-04-25 14:58:29
 */
@Service
@Slf4j
public class ResumeServiceImpl extends ServiceImpl<ResumeMapper, Resume>
        implements ResumeService {
    @Resource
    private UserService userService;

    @Resource
    private CosManager cosManager;
    @Resource
    private ResumeContentService resumeContentService;
    @Resource
    private PositionService positionService;
    @Resource
    private ResumeAnalysisResultService resumeAnalysisResultService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private FullProcessInterviewService fullProcessInterviewService;
    @Resource
    private FullProcessAnalyzeResultService fullProcessAnalyzeResultService;
    @Resource
    private CompanyService companyService;

    public static final String RESUME_PREFIX = "ai_interview/resume_pdf";

    /**
     * 简历上传
     *
     * @param multipartFile   简历文件数据
     * @param request
     * @param isFull          是否为全流程面试
     * @param fullInterviewId 全流程面试id
     * @return
     */
    @Override
    public Long uploadFile(MultipartFile multipartFile, UploadFileRequest uploadFileRequest,
            HttpServletRequest request, Boolean isFull, Long fullInterviewId) {
        // 校验简历
        validFile(multipartFile);

        // 获取面试岗位
        Long positionId = uploadFileRequest.getPositionId();
        Long techFieldId = uploadFileRequest.getTechFieldId();
        // 获取面试公司
        Long companyId = uploadFileRequest.getCompanyId();
        Company company = companyService.getById(companyId);
        Position position = positionService.lambdaQuery()
                .eq(Position::getId, positionId)
                .eq(Position::getTechFieldId, techFieldId)
                .one();

        User loginUser = userService.getLoginUser(request);
        // 文件目录：根据业务、用户来划分
        String uuid = RandomStringUtils.randomAlphanumeric(8);
        String filename = uuid + "-" + multipartFile.getOriginalFilename();
        String filepath = String.format("/%s/%s/%s", RESUME_PREFIX, loginUser.getId(), filename);
        File file = null;
        final String[] resumeUrl = new String[1];
        try {
            // 1.上传简历文件到COS
            file = File.createTempFile(filepath, null);
            multipartFile.transferTo(file);
            cosManager.putObject(filepath, file);
            resumeUrl[0] = FileConstant.COS_HOST + filepath;
            // 构造简历实例
            Resume resume = getResume(multipartFile, company.getName(), position.getName(), resumeUrl[0], loginUser,
                    isFull);
            // 保存简历
            save(resume);

            // 判断是否为全流程面试
            if (isFull) {
                // 回写简历id和简历url
                fullProcessInterviewService.lambdaUpdate()
                        .eq(FullProcessInterview::getId, fullInterviewId)
                        .set(FullProcessInterview::getResumeId, resume.getId())
                        .set(FullProcessInterview::getResumeUrl, resumeUrl[0])
                        .update();
            }

            // 2.异步获取简历内容
            CompletableFuture.runAsync(() -> {
                // 构造简历内容实例
                ResumeData resumeData = ResumeContentExtraction.resumeExtraction(resumeUrl[0]);
                log.info("resumeData:{}", resumeData);
                ResumeContent resumeContent = getResumeContent(resumeData, resume);
                // 上传简历内容
                resumeContentService.save(resumeContent);
            }).exceptionally(ex -> {
                log.error("简历内容处理异常：", ex);
                return null;
            });

            // 3.异步分析简历
            CompletableFuture.runAsync(() -> {
                // 异步分析简历
                analyzeResume(resume.getId(), loginUser.getId(), position.getName(), company.getName(), resumeUrl[0],
                        isFull, fullInterviewId);
            }).exceptionally(ex -> {
                log.error("分析简历异常：", ex);
                return null;
            });
            return resume.getId();
        } catch (Exception e) {
            log.error("文件上传失败, filepath = " + filepath, e);
            // 删除cos中的文件
            cosManager.deleteObject(filepath);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不要上传相同名称的简历");
        } finally {
            if (file != null) {
                // 删除临时文件
                boolean delete = file.delete();
                if (!delete) {
                    log.error("文件删除失败, filepath = {}", filepath);
                }
            }
        }
    }

    /**
     * 获取用户简历信息
     *
     * @param request
     * @return
     */
    @Override
    public List<ResumeVO> searchResume(ResumeSearchRequest resumeSearchRequest, HttpServletRequest request) {
        String resumeName = resumeSearchRequest.getResumeName();
        QueryWrapper<Resume> queryWrapper = new QueryWrapper<>();
        User loginUser = userService.getLoginUser(request);
        queryWrapper.like(StringUtils.isNotBlank(resumeName), "resumeName", resumeName);
        queryWrapper.eq("userId", loginUser.getId());
        queryWrapper.eq("isFull", IsFullInterviewEnum.IS_NOT_FULL_INTERVIEW);
        queryWrapper.orderByDesc("createTime");
        List<Resume> resumeList = list(queryWrapper);
        return getResumeVO(resumeList);
    }

    @Override
    public ResumeVO getResumeVO(Resume resume) {
        if (resume == null) {
            return null;
        }
        ResumeVO resumeVO = new ResumeVO();
        BeanUtils.copyProperties(resume, resumeVO);
        return resumeVO;
    }

    @Override
    public List<ResumeVO> getResumeVO(List<Resume> resumeList) {
        if (CollUtil.isEmpty(resumeList)) {
            return new ArrayList<>();
        }
        return resumeList.stream().map(this::getResumeVO).collect(Collectors.toList());
    }

    /**
     * 获取解析完成的简历(因为只有解析完成的简历才能被用于面试)
     *
     * @param request
     * @return
     */
    @Override
    public List<ExtractedResumePO> getExtractedResumeByUserId(HttpServletRequest request) {
        Long userId = userService.getLoginUser(request).getId();
        // 调用自定义SQL
        return getBaseMapper().selectExtractedResumeByUserId(userId);
    }

    /**
     * 删除简历
     *
     * @param resumeId
     * @param request
     * @return
     */
    @Override
    public boolean deleteResume(Long resumeId, HttpServletRequest request) {
        Resume resume = getById(resumeId);
        String resumeUrl = resume.getResumeUrl();
        String regex = "^https://zx-1343343346\\.cos\\.ap-chongqing\\.myqcloud\\.com/(.*)$";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(resumeUrl);

        if (matcher.find()) {
            resumeUrl = matcher.group(1);
        } else {
            System.out.println("URL 不匹配指定的格式");
        }
        String finalResumeUrl = resumeUrl;

        // 开启方法事务
        transactionTemplate.execute(status -> {
            try {
                // 删除简历
                removeById(resumeId);
                // 删除简历内容
                QueryWrapper<ResumeContent> resumeContentQueryWrapper = new QueryWrapper<>();
                resumeContentQueryWrapper.eq("resumeId", resumeId);
                resumeContentService.remove(resumeContentQueryWrapper);
                // 删除简历分析结果
                QueryWrapper<ResumeAnalysisResult> resumeAnalysisResultQueryWrapper = new QueryWrapper<>();
                resumeAnalysisResultQueryWrapper.eq("resumeId", resumeId);
                resumeAnalysisResultService.remove(resumeAnalysisResultQueryWrapper);

                // 删除简历
                cosManager.deleteObject(finalResumeUrl);
                log.info("删除简历成功：" + finalResumeUrl);
            } catch (Exception e) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除简历失败：" + e.getMessage());
            }
            return null;
        });
        return true;
    }

    /**
     * 公共接口：同步上传简历并分析
     * @param multipartFile 简历文件
     * @param positionName 岗位名称
     * @param companyName 公司名称
     * @return 包含简历ID和分析结果的Map
     */
    @Override
    public String uploadAndAnalyzeResume(MultipartFile multipartFile, String positionName, String companyName) {
        // 校验文件
        validFile(multipartFile);

        // 文件上传路径
        String uuid = RandomStringUtils.randomAlphanumeric(8);
        String filename = uuid + "-" + multipartFile.getOriginalFilename();
        String filepath = String.format("/%s/%s/%s", RESUME_PREFIX, "public", filename);
        File file = null;
        String resumeUrl = "";
        
        try {
            // 1. 上传简历文件到COS
            file = File.createTempFile(filepath, null);
            multipartFile.transferTo(file);
            cosManager.putObject(filepath, file);
            resumeUrl = FileConstant.COS_HOST + filepath;
            
            // 2. 分析简历
            Map<String, Object> analysisResult = ResumeAnalyze.analyzeResume(resumeUrl, positionName, companyName);

            return JSON.toJSONString(analysisResult);

        } catch (Exception e) {
            log.error("同步简历上传分析失败", e);
            // 删除cos中的文件
            if (file != null && StringUtils.isNotBlank(filepath)) {
                cosManager.deleteObject(filepath);
            }
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "简历上传分析失败：" + e.getMessage());
        } finally {
            if (file != null) {
                // 删除临时文件
                boolean delete = file.delete();
                if (!delete) {
                    log.error("临时文件删除失败, filepath = {}", filepath);
                }
            }
        }
    }

    /**
     * 分析简历
     *
     * @param resumeId        简历id
     * @param userId          用户id
     * @param position        面试岗位
     * @param company         面试公司
     * @param resumeUrl       简历url
     * @param isFull          是否为全流程面试
     * @param fullInterviewId 全流程面试id
     */
    private void analyzeResume(Long resumeId, Long userId, String position, String company, String resumeUrl,
            Boolean isFull, Long fullInterviewId) {

        // 调用简历分析工作流
        Map<String, Object> map = ResumeAnalyze.analyzeResume(resumeUrl, position, company);
        if (map == null) {
            log.error("简历分析返回结果为空");
            return;
        }

        // 计算总分
        List<ResumeAnalyze.DimensionInfo> dimensions = (List<ResumeAnalyze.DimensionInfo>) map.get("维度得分与理由");
        int totalScore = dimensions.stream().mapToInt(d -> d.score).sum();

        // 构建分析结果实体
        ResumeAnalysisResult result = new ResumeAnalysisResult();
        result.setResumeId(resumeId);
        result.setUserId(userId);
        result.setTotalScore(totalScore);
        result.setWeightConfigReason((String) map.get("维度权重配置理由"));

        // 设置各维度信息
        for (ResumeAnalyze.DimensionInfo dim : dimensions) {
            String dimensionName = dim.name.trim(); // 去掉可能的空白字符

            switch (dimensionName) {
                case "教育背景":
                    result.setEducationBackgroundName(dim.name);
                    result.setEducationBackgroundScore(dim.score);
                    result.setEducationBackgroundMaxScore(dim.maxScore);
                    result.setEducationBackgroundReason(dim.reason);
                    break;
                case "技能匹配":
                    result.setSkillMatchName(dim.name);
                    result.setSkillMatchScore(dim.score);
                    result.setSkillMatchMaxScore(dim.maxScore);
                    result.setSkillMatchReason(dim.reason);
                    break;
                case "项目经验":
                    result.setProjectExperienceName(dim.name);
                    result.setProjectExperienceScore(dim.score);
                    result.setProjectExperienceMaxScore(dim.maxScore);
                    result.setProjectExperienceReason(dim.reason);
                    break;
                case "岗位匹配":
                case "岗位整体匹配": // 兼容工作流返回的不同格式
                    result.setPositionMatchName(dim.name);
                    result.setPositionMatchScore(dim.score);
                    result.setPositionMatchMaxScore(dim.maxScore);
                    result.setPositionMatchReason(dim.reason);
                    break;
                case "潜力成长":
                case "潜力成长性": // 兼容工作流返回的不同格式
                    result.setPotentialGrowthName(dim.name);
                    result.setPotentialGrowthScore(dim.score);
                    result.setPotentialGrowthMaxScore(dim.maxScore);
                    result.setPotentialGrowthReason(dim.reason);
                    break;
                case "协作与领导":
                case "协作领导": // 兼容工作流返回的不同格式
                    result.setTeamLeadershipName(dim.name);
                    result.setTeamLeadershipScore(dim.score);
                    result.setTeamLeadershipMaxScore(dim.maxScore);
                    result.setTeamLeadershipReason(dim.reason);
                    break;
                case "荣誉奖项":
                    result.setCertificatesName(dim.name);
                    result.setCertificatesScore(dim.score);
                    result.setCertificatesMaxScore(dim.maxScore);
                    result.setCertificatesReason(dim.reason);
                    break;
                case "沟通表达":
                    result.setCommunicationName(dim.name);
                    result.setCommunicationScore(dim.score);
                    result.setCommunicationMaxScore(dim.maxScore);
                    result.setCommunicationReason(dim.reason);
                    break;
                default:
                    // 记录未匹配的维度名称，便于调试
                    log.warn("未匹配的维度名称: [{}]", dimensionName);
                    break;
            }
        }

        // 设置其他分析结果
        result.setAdvantages((String) map.get("优势"));
        result.setDisadvantages((String) map.get("短板与核心建议"));
        result.setRisks((String) map.get("潜在风险"));
        result.setImprovementSuggestions((String) map.get("简历改进建议"));

        // 保存分析结果
        resumeAnalysisResultService.save(result);

        // 修改简历分析状态和回写简历分析id
        lambdaUpdate().eq(Resume::getId, resumeId)
                .set(Resume::getResumeStatus, AnalyzeStatusEnum.COMPLETED.getValue())
                .set(Resume::getResumeAnalyzeResultId, result.getId())
                .update();

        // 判断是否为全流程面试(回写简历分析id)
        if (isFull) {
            fullProcessAnalyzeResultService.lambdaUpdate()
                    .eq(FullProcessAnalyzeResult::getFullInterviewId, fullInterviewId)
                    .set(FullProcessAnalyzeResult::getResumeAnalysisId, result.getId())
                    .update();
        }
    }

    /**
     * 构造简历内容实例
     *
     * @param resumeData
     * @return
     */
    private ResumeContent getResumeContent(ResumeData resumeData, Resume resume) {
        ResumeContent resumeContent = new ResumeContent();
        resumeContent.setResumeId(resume.getId());
        resumeContent.setEducation(resumeData.getEducation());
        resumeContent.setSkill(resumeData.getSkill());
        resumeContent.setInternship(resumeData.getInternship());
        resumeContent.setProjectExperience(resumeData.getProjectExperience());
        resumeContent.setWorkExperience(resumeData.getWorkExperience());
        resumeContent.setSchoolExperience(resumeData.getSchoolExperience());
        resumeContent.setCertificate(resumeData.getCertificate());
        resumeContent.setEvaluation(resumeData.getEvaluation());
        resumeContent.setUserId(resume.getUserId());
        return resumeContent;
    }

    /**
     * 封装简历实例
     *
     * @param multipartFile
     * @param company
     * @param position
     * @param resumeUrl
     * @param loginUser
     * @param isFull
     * @return
     */
    @NotNull
    private Resume getResume(MultipartFile multipartFile, String company, String position, String resumeUrl,
            User loginUser, Boolean isFull) {
        String originalFilename = multipartFile.getOriginalFilename();
        int lastDotIndex = Objects.requireNonNull(originalFilename).lastIndexOf('.');
        String fileNameWithoutExtension = originalFilename.substring(0, lastDotIndex);

        // 将简历信息写入数据库
        Resume resume = new Resume();
        resume.setCompany(company);
        resume.setPosition(position);
        resume.setResumeName(fileNameWithoutExtension);
        resume.setResumeUrl(resumeUrl);
        resume.setUserId(loginUser.getId());
        resume.setResumeStatus(AnalyzeStatusEnum.ANALYZING.getValue());
        if (isFull)
            resume.setIsFull(1);
        else
            resume.setIsFull(0);
        return resume;
    }

    /**
     * 校验文件
     *
     * @param multipartFile 简历
     */
    private void validFile(MultipartFile multipartFile) {
        // 文件大小
        long fileSize = multipartFile.getSize();
        // 文件后缀
        String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());
        final long ONE_M = 1024 * 1024L;
        if (fileSize > ONE_M) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M");
        }
        if (!Objects.equals("pdf", fileSuffix)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误");
        }
    }
}